package xyz.xtt.schedule.service;

import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.time.Instant;
import java.util.List;
import java.util.Objects;
import java.util.Random;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

import org.apache.commons.lang3.StringUtils;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.web3j.protocol.core.methods.response.EthBlock;
import org.web3j.utils.Numeric;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.primitives.Longs;
import com.tongtong.middleware.vo.params.AccountParams;
import com.tongtong.middleware.vo.params.nft.BurnNFTParams;
import com.tongtong.middleware.vo.params.nft.MintNFTParams;
import com.tongtong.middleware.vo.params.nft.Proposal;
import com.tongtong.middleware.vo.params.nft.TransferParams;
import com.tongtong.middleware.vo.response.AccountResponse;
import com.tongtong.middleware.vo.response.TransactionMergeData;

import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import xyz.xtt.schedule.constants.AssetTypeEnum;
import xyz.xtt.schedule.entity.AssetEntity;
import xyz.xtt.schedule.entity.AssetExtEntity;
import xyz.xtt.schedule.entity.AssetTransactionEntity;
import xyz.xtt.schedule.mapper.asset.AssetTransactionMapper;
import xyz.xtt.schedule.message.AssetEventMessage;
import xyz.xtt.schedule.utils.RedisLockUtil;
import xyz.xtt.schedule.vo.UpdateChainHash;

/**
 * @author wangqi
 */
@RefreshScope
@Service
@Slf4j
public class OnChainService extends ServiceImpl<AssetTransactionMapper, AssetTransactionEntity> {
	@Autowired
	private InnerService innerService;
	@Autowired
	AssetService assetService;
	@Autowired
	AssetExtService assetExtService;
	@Autowired
	StringRedisTemplate stringRedisTemplate;
	@Autowired
	AssetTransactionService assetTransactionService;
	@Value("${rocketmq.producer.asseteventmsg}")
	private String assetEventMsg;
	@Value("${bsn.sandbox}")
	private boolean sandbox;
	@Autowired
	private RocketMQTemplate rocketMQTemplate;
	@Autowired
	private RedisLockUtil redisLockUtil;
	private static final String LOCK_KEY_REFRESH_ON_CHAIN = "NFT:LOCK:REFRESH:ON:CHAIN";
	private static final String TRANSFER_HASH_QUEUE = "SCHEDULED:QUEUE:TRANSFER:HASH";
	private static final String MINT_HASH_QUEUE = "SCHEDULED:QUEUE:MINT:HASH";
	private static final int DELAY_QUEUE = 15000;

	public void onChainTask() {
		String lockValue = UUID.randomUUID().toString();
		boolean locked = redisLockUtil.tryLock(LOCK_KEY_REFRESH_ON_CHAIN, lockValue, 2, TimeUnit.MINUTES, 10, TimeUnit.SECONDS);
		if (locked) {
			findMintInfo();
			findTransferInfo();
			findBurnInfo();
			redisLockUtil.releaseLock(LOCK_KEY_REFRESH_ON_CHAIN, lockValue);
		}
	}

	/**
	 * 查询待铸造的数字资产
	 */
	public void findMintInfo() {
		// 查找token_id大于0，并且metadata不为空的数据
		List<AssetExtEntity> assetExtList = assetExtService.findMintList();
		log.info("findMintInfo: 找到{}条记录", assetExtList.size());
		// 触发上链动作
		for (AssetExtEntity entity : assetExtList) {
			log.info("【成功】findMintInfo: 开始准备铸造 {}", entity.getTokenId());
			// 查询是否铸造过
			String mintIdOnChain = stringRedisTemplate.opsForValue().get("SCHEDULED:MINT:" + entity.getId());
			if (mintIdOnChain != null) {
				log.error("【失败】findMintInfo: 已经铸造过 getRedis {}", entity.getTokenId());
				continue;
			}
			// 根据assetId查询拥有者ID和铸造数量
			AssetEntity assetEntity = assetService.findByAssetId(entity.getAssetId().toString());
			// 修正扩展表和资产表数据不一致的问题
			if (assetEntity == null) {
				log.error("【失败】findMintInfo findByAssetId: 资产表里无AssetId {}", entity.getAssetId());
				continue;
			}
			AccountParams accountParams = new AccountParams();
			accountParams.setUserId(assetEntity.getOwnerId());
			AccountResponse accountRes = innerService.createAndQueryAccount(accountParams);
			if (Objects.isNull(accountRes)) {
				log.error("【失败】findMintInfo createAndQueryAccount: userID {} is null", assetEntity.getOwnerId());
				continue;
			}
			String address = accountRes.getAddress();
			// 判断地址是否正确
			if (address != null && address.length() != 42) {
				log.error("【失败】findMintInfo createAndQueryAccount: userID {} address {}", assetEntity.getOwnerId(), address);
				continue;
			}
			Proposal proposal = null;
			if ("1155".equals(entity.getReferProtocol())) {
				proposal = Proposal.ERC1155;
			} else if ("721".equals(entity.getReferProtocol())) {
				proposal = Proposal.ERC721;
				// 如果是721 ，铸造数量不能大于1
//				if (assetEntity.getAmount() > 1) {
//					log.error("【失败】findMintInfo 判断protocol: proposal {} amount {}", proposal, assetEntity.getAmount());
//					continue;
//				}
			}
			// 此处增加沙盒开关查询
//			ApiResult<String> hashRes;
			String hashRes = StringUtils.EMPTY;
			if (sandbox) {
				byte[] hash;
				try {
					MessageDigest digest = MessageDigest.getInstance("SHA3-256");
					hash = digest.digest(Long.toString(entity.getId()).getBytes());
				} catch (NoSuchAlgorithmException e) {
					throw new RuntimeException(e);
				}
				hashRes = Numeric.toHexString(hash);
			} else {
				MintNFTParams mintNftParams = new MintNFTParams();
				mintNftParams.setProposal(proposal);
				mintNftParams.setTo(address);
				mintNftParams.setTokenId(new BigInteger(String.valueOf(entity.getTokenId())));
				mintNftParams.setAmount(new BigInteger(String.valueOf(assetEntity.getAmount())));
				hashRes = innerService.mintNFT(mintNftParams);
			}
			if (StringUtils.isNotBlank(hashRes)) {
				String hash = hashRes;
				// 记录到redis 防止错误引发频繁上链
				stringRedisTemplate.opsForValue().set("SCHEDULED:MINT:" + entity.getId(), hash);
				stringRedisTemplate.expire("SCHEDULED:MINT:" + entity.getId(), 12, TimeUnit.HOURS);
				// 更新上链hash
				assetExtService.updateHashByTokenId(entity.getTokenId().toString(), hash);
				// 加入到延迟队列
				double score = Instant.now().toEpochMilli() + DELAY_QUEUE;
				UpdateChainHash updateChainHash = new UpdateChainHash(entity.getAssetId().toString(), hash);
				String jsonStr = JSONUtil.toJsonStr(updateChainHash);
				stringRedisTemplate.opsForZSet().add(MINT_HASH_QUEUE, jsonStr, score);
				// 老胡需要，铸造时加
				syncChainUpMessage(entity, assetEntity, hash);
			} else {
				log.error("【失败】findMintInfo mintNFT tokenId {} error {}", entity.getTokenId(), hashRes);
			}
		}
	}

	/**
	 * 查询待转移的数字资产
	 */
	public void findTransferInfo() {
		// 1、查询已支付未上链信息
		List<AssetTransactionEntity> assetTransactionList = assetTransactionService.findTransferList();
		log.info("findTransferInfo: 找到 {} 条记录", assetTransactionList.size());
		// 2、触发上链动作
		for (AssetTransactionEntity entity : assetTransactionList) {
			log.info("【成功】findTransferInfo: 开始准备转移OrderId {}", entity.getOrderId());
			// 查询是否转移过
			String orderIdOnChain = stringRedisTemplate.opsForValue().get("SCHEDULED:TRANSFER:" + entity.getOrderId());
			if (orderIdOnChain != null) {
				log.error("【失败】findTransferInfo: 已经转移过 getRedis {}", entity.getOrderId());
				continue;
			}
			AssetExtEntity assetExtEntity = assetExtService.findByAssetId(entity.getAssetId());
			// 处理错误数据的问题
			if (assetExtEntity == null || assetExtEntity.getReferProtocol() == null || assetExtEntity.getTokenId() < 1) {
				log.error("【失败】findTransferInfo: 资产过站表无此资产 AssetId {}", entity.getAssetId());
				continue;
			}
			// 处理协议
			Proposal proposal = null;
			if ("1155".equals(assetExtEntity.getReferProtocol())) {
				proposal = Proposal.ERC1155;
			} else if ("721".equals(assetExtEntity.getReferProtocol())) {
				proposal = Proposal.ERC721;
				// 如果是721 ，转移数量不能大于1
//				if (entity.getAmount().intValue() > 1) {
//					log.error("【失败】findTransferInfo 判断protocol: proposal {} amount {}", proposal, entity.getAmount());
//					continue;
//				}
			}
			// 处理卖方地址
			AccountParams accountParams = new AccountParams();
			accountParams.setUserId(entity.getSellerUserId());
			AccountResponse sellerRes = innerService.createAndQueryAccount(accountParams);
			String sellerAddress = sellerRes.getAddress();
			// 处理买方地址
			accountParams.setUserId(entity.getBuyerUserId());
			AccountResponse buyerRes = innerService.createAndQueryAccount(accountParams);
			String buyerAddress = buyerRes.getAddress();
			// 判断地址是否正确
			if ((sellerAddress != null && sellerAddress.length() != 42) || (buyerAddress != null && buyerAddress.length() != 42)) {
				log
						.error("【失败】findTransferInfo createAndQueryAccount: sellerUserID {} sellerAddress {} buyerUserID {} buyerAddress {}", entity
								.getSellerUserId(), sellerAddress, entity.getBuyerUserId(), buyerAddress);
				continue;
			}
			// 根据assetId查询拥有者ID和铸造数量
			AssetEntity assetEntity = assetService.findByAssetId(entity.getAssetId());
			// 修正扩展表和资产表数据不一致的问题
			if (assetEntity == null) {
				log.error("【失败】findMintInfo findByAssetId: 资产表里无AssetId {}", entity.getAssetId());
				continue;
			}
			// 此处增加沙盒开关查询
			String hashRes = StringUtils.EMPTY;
			if (sandbox) {
				byte[] hash;
				try {
					MessageDigest digest = MessageDigest.getInstance("SHA3-256");
					hash = digest.digest(entity.getOrderId().getBytes());
				} catch (NoSuchAlgorithmException e) {
					throw new RuntimeException(e);
				}
				hashRes = Numeric.toHexString(hash);
			} else {
				// 发送转移请求
				TransferParams transferParams = new TransferParams();
				transferParams.setProposal(proposal);
				transferParams.setFrom(sellerAddress);
				transferParams.setTo(buyerAddress);
				transferParams.setTokenId(new BigInteger(String.valueOf(assetExtEntity.getTokenId())));
				transferParams.setAmount(entity.getAmount());
				hashRes = innerService.transfer(transferParams);
			}
			if (StringUtils.isNotBlank(hashRes)) {
				String hash = hashRes;
				// 记录到redis 防止错误引发频繁上链
				stringRedisTemplate.opsForValue().set("SCHEDULED:TRANSFER:" + entity.getOrderId(), hash);
				stringRedisTemplate.expire("SCHEDULED:TRANSFER:" + entity.getOrderId(), 12, TimeUnit.HOURS);
				// 更新上链hash
				assetTransactionService.updateHashByOrderId(entity, hash);
				// 加入到延迟队列
				double score = Instant.now().toEpochMilli() + DELAY_QUEUE;
				UpdateChainHash updateChainHash = new UpdateChainHash(entity.getOrderId(), hash);
				String jsonStr = JSONUtil.toJsonStr(updateChainHash);
				stringRedisTemplate.opsForZSet().add(TRANSFER_HASH_QUEUE, jsonStr, score);
				syncTransferMessage(entity, assetEntity, hash);
			} else {
				log
						.error("【失败】findTransferInfo transfer proposal:{} sellerAddress:{} buyerAddress:{} tokenId:{} amount:{} error {}", proposal, sellerAddress, buyerAddress, assetExtEntity
								.getTokenId(), entity.getAmount(), hashRes);
			}
		}
	}

	/**
	 * 查询待销毁的数字资产
	 */
	public void findBurnInfo() {
		// 1、查询待销毁的数字资产
		List<AssetTransactionEntity> assetTransactionList = assetTransactionService.findBurnList();
		log.info("findBurnInfo: 找到 {} 条记录", assetTransactionList.size());
		// 2、触发上链动作
		for (AssetTransactionEntity entity : assetTransactionList) {
			log.info("【成功】findBurnInfo: 开始准备销毁OrderId {}", entity.getOrderId());
			// 查询是否转移过
			String orderIdOnChain = stringRedisTemplate.opsForValue().get("SCHEDULED:BURN:" + entity.getOrderId());
			if (orderIdOnChain != null) {
				log.error("【失败】findBurnInfo: 已经销毁过 getRedis {}", entity.getOrderId());
				continue;
			}
			AssetExtEntity assetExtEntity = assetExtService.findByAssetId(entity.getAssetId());
			// 处理错误数据的问题
			if (assetExtEntity == null || assetExtEntity.getReferProtocol() == null || assetExtEntity.getTokenId() < 1) {
				log.error("【失败】findBurnInfo: 资产扩展表无此资产 AssetId {}", entity.getAssetId());
				continue;
			}
			// 处理协议
			Proposal proposal = null;
			if ("1155".equals(assetExtEntity.getReferProtocol())) {
				proposal = Proposal.ERC1155;
			} else if ("721".equals(assetExtEntity.getReferProtocol())) {
				proposal = Proposal.ERC721;
//				if (entity.getAmount().intValue() > 1) {
//					log.error("【失败】findBurnInfo 判断protocol: proposal {} amount {}", proposal, entity.getAmount());
//					continue;
//				}
			}
			// 处理卖方地址
			AccountParams accountParams = new AccountParams();
			accountParams.setUserId(entity.getSellerUserId());
			AccountResponse sellerRes = innerService.createAndQueryAccount(accountParams);
			String sellerAddress = sellerRes.getAddress();
			// 判断地址是否正确
			if (sellerAddress != null && sellerAddress.length() != 42) {
				log.error("【失败】findBurnInfo createAndQueryAccount: sellerUserID {} sellerAddress {}", entity.getSellerUserId(), sellerAddress);
				continue;
			}
			// 根据assetId查询拥有者ID和铸造数量
			AssetEntity assetEntity = assetService.findByAssetId(entity.getAssetId());
			// 修正扩展表和资产表数据不一致的问题
			if (assetEntity == null) {
				log.error("【失败】findBurnInfo findByAssetId: 资产表里无AssetId {}", entity.getAssetId());
				continue;
			}
			// 此处增加沙盒开关查询
			String hashRes;
			if (sandbox) {
				byte[] hash;
				try {
					MessageDigest digest = MessageDigest.getInstance("SHA3-256");
					hash = digest.digest(entity.getOrderId().getBytes());
				} catch (NoSuchAlgorithmException e) {
					throw new RuntimeException(e);
				}
				hashRes = Numeric.toHexString(hash);
			} else {
				// 发送销毁请求
				BurnNFTParams burnNftParams = new BurnNFTParams();
				burnNftParams.setProposal(proposal);
				burnNftParams.setAccount(sellerAddress);
				burnNftParams.setTokenId(new BigInteger(String.valueOf(assetExtEntity.getTokenId())));
				burnNftParams.setAmount(entity.getAmount());
				hashRes = innerService.burnNFT(burnNftParams);
			}
			if (StringUtils.isNotBlank(hashRes)) {
				String hash = hashRes;
				// 记录到redis 防止错误引发频繁上链
				stringRedisTemplate.opsForValue().set("SCHEDULED:BURN:" + entity.getOrderId(), hash);
				stringRedisTemplate.expire("SCHEDULED:BURN:" + entity.getOrderId(), 12, TimeUnit.HOURS);
				// 更新上链hash
				assetTransactionService.updateHashByOrderId(entity, hash);
				// 加入到延迟队列
				double score = Instant.now().toEpochMilli() + DELAY_QUEUE;
				UpdateChainHash updateChainHash = new UpdateChainHash(entity.getOrderId(), hash);
				String jsonStr = JSONUtil.toJsonStr(updateChainHash);
				// 转移跟销毁使用同一队列
				stringRedisTemplate.opsForZSet().add(TRANSFER_HASH_QUEUE, jsonStr, score);
				syncBurnMessage(entity, assetEntity, hash);
			} else {
				log
						.error("【失败】findBurnInfo burn proposal:{} sellerAddress:{} tokenId:{} amount:{} error {}", proposal, sellerAddress, assetExtEntity
								.getTokenId(), entity.getAmount(), hashRes);
			}
		}
	}

	private void syncTransferMessage(AssetTransactionEntity entity, AssetEntity asset, String hash) {
		log.info("syncTransferMessage message start bizId: {}", asset.getGoodsId());
		try {
			AssetTypeEnum assetType = AssetTypeEnum.queryConvert(asset.getFirstType());
			if (Objects.isNull(assetType)) {
				log.info("syncTransferMessage message asset: {}, firstType is blank", asset.getId());
				return;
			}
			String bizType = asset.getBizType();
			if (StringUtils.isBlank(bizType)) {
				log.info("syncTransferMessage message asset: {}, bizType is blank", asset.getId());
				return;
			}
			AssetEventMessage buildTransferFromEvent = AssetEventMessage
					.buildTransferFromEvent(asset.getId(), Longs.tryParse(entity.getSellerUserId()), Longs.tryParse(entity.getBuyerUserId()), hash, Longs
							.tryParse(entity.getOrderId()), asset.getGoodsId(), assetType.name());
			sendMessage(buildTransferFromEvent, bizType);
			log.info("syncTransferMessage message end bizId: {}", asset.getGoodsId());
		} catch (Exception e) {
			log.info("syncTransferMessage ex: {}", e.getMessage(), e);
		}
	}

	private void syncChainUpMessage(AssetExtEntity assetExt, AssetEntity asset, String hash) {
		log.info("syncChainUpMessage message start bizId: {}", asset.getGoodsId());
		try {
			AssetTypeEnum assetType = AssetTypeEnum.queryConvert(asset.getFirstType());
			if (Objects.isNull(assetType)) {
				log.info("syncChainUpMessage message asset: {}, firstType is blank", asset.getId());
				return;
			}
			String bizType = asset.getBizType();
			if (StringUtils.isBlank(bizType)) {
				log.info("syncChainUpMessage message asset: {}, bizType is blank", asset.getId());
				return;
			}
			AssetEventMessage buildMinEvent = AssetEventMessage
					.buildMinEvent(asset.getId(), Longs.tryParse(asset.getOwnerId()), hash, assetExt.getId(), asset.getGoodsId(), assetType.name());
			sendMessage(buildMinEvent, bizType);
			log.info("syncChainUpMessage message end bizId: {} ", asset.getGoodsId());
		} catch (Exception e) {
			log.info("syncChainUpMessage ex: {}", e.getMessage(), e);
		}
	}

	private void syncBurnMessage(AssetTransactionEntity entity, AssetEntity asset, String hash) {
		log.info("syncBurnMessage message start bizId: {}", asset.getGoodsId());
		try {
			AssetTypeEnum assetType = AssetTypeEnum.queryConvert(asset.getFirstType());
			if (Objects.isNull(assetType)) {
				log.info("syncBurnMessage message asset: {}, firstType is blank", asset.getId());
				return;
			}
			String bizType = asset.getBizType();
			if (StringUtils.isBlank(bizType)) {
				log.info("syncBurnMessage message asset: {}, bizType is blank", asset.getId());
				return;
			}
			AssetEventMessage buildTransferFromEvent = AssetEventMessage
					.buildBurnEvent(asset.getId(), Longs.tryParse(entity.getSellerUserId()), hash, Longs.tryParse(entity.getOrderId()), asset
							.getGoodsId(), assetType.name());
			sendMessage(buildTransferFromEvent, bizType);
			log.info("syncBurnMessage message end bizId: {}", asset.getGoodsId());
		} catch (Exception e) {
			log.info("syncBurnMessage ex: {}", e.getMessage(), e);
		}
	}

	private void sendMessage(AssetEventMessage buildMinEvent, String tag) {
		String topic = String.format("%s:%s", assetEventMsg, tag);
		log.info("schedule sendMessage topic:{},type:{},msg:{}", topic, buildMinEvent.getEventName(), JSONUtil.toJsonStr(buildMinEvent));
		rocketMQTemplate.convertAndSend(topic, JSONUtil.toJsonStr(buildMinEvent));
	}

	/**
	 * 1、查询交易表里hash=66并且上链时间为0的记录改为从redis延迟队列里查询 2、根据hash查询链上数据，需要判断智能合约执行状态
	 * 3、如果执行成功，则获取到数据后更新tbl_asset_ext和tbl_asset_transaction表的链数据
	 * 4、如果执行失败，打印日志需要人工排查
	 */
	public void updateChainStatus() {
		double now = Instant.now().toEpochMilli();
		// 先更新铸造的交易
		Set<String> mintMessages = stringRedisTemplate.opsForZSet().rangeByScore(MINT_HASH_QUEUE, 0, now);
		if (mintMessages != null) {
			for (String message : mintMessages) {
				// 处理消息
				UpdateChainHash updateChainHash = JSONUtil.toBean(message, UpdateChainHash.class);
				TransactionMergeData transactionMergeData;
				if (sandbox) {
					transactionMergeData = new TransactionMergeData();
					transactionMergeData.setStatus("0x1");
					Random random = new Random();
					int randomNumber = random.nextInt(90000000) + 10000000;
					transactionMergeData.setBlockNumber(BigInteger.valueOf(randomNumber));
				} else {
					transactionMergeData = innerService.getTransactionInfo(updateChainHash.getHash());
				}
				// 需要判断交易是否为空，因为有可能没上链
				if (transactionMergeData != null && transactionMergeData.getStatus() != null) {
					if ("0x1".equals(transactionMergeData.getStatus())) {
						EthBlock.Block block;
						if (sandbox) {
							block = new EthBlock.Block();
							block.setTimestamp(Long.toString(Instant.now().getEpochSecond()));
						} else {
							block = innerService.getBlockByNumber(transactionMergeData.getBlockNumber());
						}
						assetExtService
								.updateAssetChainByAssetId(updateChainHash.getId(), updateChainHash
										.getHash(), transactionMergeData.getBlockNumber().toString(), block.getTimestamp().intValue());
					} else {
						log.error("【失败】updateChainStatus 上链异常: 铸造记录assetId {} 上链hash=>{}", updateChainHash.getId(), updateChainHash.getHash());
					}
				}
				stringRedisTemplate.opsForZSet().remove(MINT_HASH_QUEUE, message);
			}
		}
		// 再更新转移的交易
		Set<String> transferMessages = stringRedisTemplate.opsForZSet().rangeByScore(TRANSFER_HASH_QUEUE, 0, now);
		if (transferMessages != null) {
			for (String message : transferMessages) {
				// 处理消息
				UpdateChainHash updateChainHash = JSONUtil.toBean(message, UpdateChainHash.class);
				TransactionMergeData transactionMergeData;
				if (sandbox) {
					transactionMergeData = new TransactionMergeData();
					transactionMergeData.setStatus("0x1");
					Random random = new Random();
					int randomNumber = random.nextInt(90000000) + 10000000;
					transactionMergeData.setBlockNumber(BigInteger.valueOf(randomNumber));
				} else {
					transactionMergeData = innerService.getTransactionInfo(updateChainHash.getHash());
				}
				// 需要判断交易是否为空，因为有可能没上链
				if (transactionMergeData != null && transactionMergeData.getStatus() != null) {
					if ("0x1".equals(transactionMergeData.getStatus())) {
						EthBlock.Block block;
						if (sandbox) {
							block = new EthBlock.Block();
							block.setTimestamp(Long.toString(Instant.now().getEpochSecond()));
						} else {
							block = innerService.getBlockByNumber(transactionMergeData.getBlockNumber());
						}
						assetTransactionService
								.updateTransactionChainHash(updateChainHash
										.getId(), transactionMergeData.getBlockNumber().toString(), block.getTimestamp().intValue());
					} else {
						log.error("【失败】updateChainStatus 上链异常: 转移记录orderId {} 上链hash=>{}", updateChainHash.getId(), updateChainHash.getHash());
					}
				}
				stringRedisTemplate.opsForZSet().remove(TRANSFER_HASH_QUEUE, message);
			}
		}
	}
}
